package cnisetup

import (
	"context"
	"fmt"
	"strings"

	"github.com/f5devcentral/f5-bigip-rest-go/utils"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	confv1 "k8s.io/client-go/applyconfigurations/core/v1"
)

func (fvc *FlannelVxlanContext) Setup() error {
	slog := utils.LogFromContext(fvc)
	k8sclient := newKubeClient(fvc.Kube.Config)
	for _, nc := range fvc.Kube.Nodes {
		nodeName := fmt.Sprintf("bigip-%s", nc.PublicIP)
		macAddr, err := fvc.searchTunnelMacWith(nc.PublicIP)
		if err != nil {
			return err
		}
		nodeConf := confv1.Node(nodeName)
		nodeConf.WithName(nodeName)
		nodeConf.WithAnnotations(map[string]string{
			"flannel.alpha.coreos.com/public-ip":           nc.PublicIP,
			"flannel.alpha.coreos.com/backend-data":        fmt.Sprintf(`{"VtepMAC":"%s"}`, macAddr),
			"flannel.alpha.coreos.com/backend-type":        "vxlan",
			"flannel.alpha.coreos.com/kube-subnet-manager": "true",
		})
		nodeConf = nodeConf.WithSpec(confv1.NodeSpec().WithPodCIDR(nc.PodCIDR))
		if _, err := k8sclient.CoreV1().Nodes().Apply(context.TODO(), nodeConf, metav1.ApplyOptions{FieldManager: "v1"}); err != nil {
			return err
		} else {
			slog.Infof("node %s created in k8s.", nodeName)
		}
	}
	return nil
}

func (fvc *FlannelVxlanContext) Teardown() error {
	slog := utils.LogFromContext(fvc)
	k8sclient := newKubeClient(fvc.Kube.Config)
	for _, nc := range fvc.Kube.Nodes {
		nodeName := fmt.Sprintf("bigip-%s", nc.PublicIP)
		if err := k8sclient.CoreV1().Nodes().Delete(context.TODO(), nodeName, metav1.DeleteOptions{}); err != nil {
			if strings.Contains(err.Error(), "not found") {
			} else {
				return err
			}
		}
		slog.Infof("node %s deleted from k8s.", nodeName)
	}
	return nil
}

func (fvc *FlannelVxlanContext) nodeConfigs() (map[string]interface{}, error) {
	slog := utils.LogFromContext(fvc)
	clientset := newKubeClient(fvc.Kube.Config)

	nodeList, err := clientset.CoreV1().Nodes().List(fvc, metav1.ListOptions{})
	if err != nil {
		slog.Errorf("failed to list nodes: %s", err.Error())
		return nil, err
	}

	cfgs := map[string]interface{}{}
	nIpToMacV4, _ := allNodeIPMacAddrs(fvc, nodeList)
	for _, tunnel := range fvc.Tunnels {
		fcfgs := parseFdbsFrom(tunnel.Name, nIpToMacV4)
		for k, v := range fcfgs {
			cfgs[k] = v
		}
	}
	return cfgs, nil
}

/******************************************************************************************************/

func (fhc *FlannelHostgwContext) Setup() error {
	return nil
}

func (fhc *FlannelHostgwContext) Teardown() error {
	return nil
}

func (fhc *FlannelHostgwContext) nodeConfigs() (map[string]interface{}, error) {
	slog := utils.LogFromContext(fhc)
	clientset := newKubeClient(fhc.Kube.Config)

	nodeList, err := clientset.CoreV1().Nodes().List(fhc, metav1.ListOptions{})
	if err != nil {
		slog.Errorf("failed to list nodes: %s", err.Error())
		return nil, err
	}

	cfgs := map[string]interface{}{}
	ipCIDRs := allNodeIPCIDRs(fhc, nodeList)
	routes := parseRoutesFrom(ipCIDRs)
	for k, v := range routes {
		cfgs[k] = v
	}

	slog.Debugf("found routes, but will not be created")
	slog.Debugf("%s", routes)
	// there would be error if gateway not directly connected
	// {"code":400,"message":"transaction failed:01070330:3: Static route gateway 172.24.0.4 is not directly connected via an interface.","errorStack":[],"apiError":2}

	return nil, nil
}
